﻿using QDAS;
using QDasConverter.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WindGoes6.Database;

namespace QDasConverter.Core
{
    /// <summary>
    /// 马头转换器。
    /// </summary>
    public class C2021T02_cvinet : ConvertBase
    {
        // 以下内容都是可以通过文件加载的，实现代码在本类的 LoadBenchNames 方法
        string[] benchNames = { "RC-0015", "DCT-0075", "DCT-0080", "DCT-0090", "DCT-0100", "DCT-0110", "DCT-0210", "DCT-0220", "DCT-0225", "DCT-0250", "DCT-0260", "DCT-0265", "DCT-0280", "DCT-0300", "DCT-0305", "DCT-0310", "DCT-0320", "DCT-0330", "DCT-0390", "DCT-0420", "DCT-0440", "DCT-0460", "DCT-0465", "DCT-0480", "DCT-0490", "DCT-0540", "DCT-0635", "DCT-0636", "DCT-0638", "MID-0110", "MID-0120", "DIFF-0100", "DIFF-0110", "DIFF-0120" };
        string[] benchTexts = { "RC-0015", "DCT-0075", "DCT-0080", "DCT-0090", "DCT-0100", "DCT-0110", "DCT-0210", "DCT-0220", "DCT-0225", "DCT-0250", "DCT-0260", "DCT-0265", "DCT-0280", "DCT-0300", "DCT-0305", "DCT-0310", "DCT-0320", "DCT-0330", "DCT-0390", "DCT-0420", "DCT-0440", "DCT-0460", "DCT-0465", "DCT-0480", "DCT-0490", "DCT-0540", "DCT-0635", "DCT-0636", "DCT-0638", "MID-0110", "MID-0120", "DIFF-0100", "DIFF-0110", "DIFF-0120" };
        string[] benchIPs = { "RC-0015", "DCT-0075", "DCT-0080", "DCT-0090", "DCT-0100", "DCT-0110", "DCT-0210", "DCT-0220", "DCT-0225", "DCT-0250", "DCT-0260", "DCT-0265", "DCT-0280", "DCT-0300", "DCT-0305", "DCT-0310", "DCT-0320", "DCT-0330", "DCT-0390", "DCT-0420", "DCT-0440", "DCT-0460", "DCT-0465", "DCT-0480", "DCT-0490", "DCT-0540", "DCT-0635", "DCT-0636", "DCT-0638", "MID-0110", "MID-0120", "DIFF-0100", "DIFF-0110", "DIFF-0120" };

        QCatalog catlog = new QCatalog();


        /// <summary>
        ///  每个元素为字符串数据，内容为： 工位名称，工位索引，Spindle和Spindle索引号。
        ///  提取规则为 工位名称和Spindel名称一致，然后提取 Spindle索引号。
        /// </summary>
        List<string[]> opindices = new List<string[]>();
        Dictionary<string, string[]> limits = new Dictionary<string, string[]>();
        Dictionary<string, string> opNames = new Dictionary<string, string>();

        /// <summary>
        /// 2022/04/05加入，为了确认工位号，目前为ID，Name，IP和K0010四列。
        /// 默认所有ID都完整，所以使用List，否则需要使用Dictionary。
        /// 原理：根据 RESULT.RES_ControllerId = dbo.CONTROLLER.CTRL_ID 进行关联，再从此配置文件中找到对应的K0010。
        /// </summary>
        List<string[]> oplist = new List<string[]>();
        string oplist_configfile = @"configs\C2021T02_cvinet_op_info.csv";

        public C2021T02_cvinet()
        {
            DisplayName = "马头转换器";
            TableName = "RESULT";
            TableIDName = "RES_ID";
            Version = "beta1 Released Date: 2021/11/09 Inner Name: C2021T02_cvinet";
            Version = "beta2 Released Date: 2021/11/09 Inner Name: C2021T02_cvinet";
            Version = "beta3 Released Date: 2021/11/15 Inner Name: C2021T02_cvinet";
            Version = "beta4 Released Date: 2022/04/05 Inner Name: C2021T02_cvinet";
            Version = "beta5 Released Date: 2022/04/09 Inner Name: C2021T02_cvinet";
            catlog = QCatalog.load(@"configs\\CATALOG.dfd");

            // 2021/12/30 临时为了测试才加入的。
            string[] data = { "工位号,OP索引号,轴号,拧紧机索引号", "RC-0010,1110010,1,1110005", "DCT-0080,1120080,1,1110010", "DCT-0090,1120090,1,1110015", "DCT-0090,1120090,2,1110020", "DCT-0100,1120100,1,1110025", "DCT-0110,1120110,1,1110030", "DCT-0220,1120220,1,1110035", "DCT-0225,1120225,1,1110040", "DCT-0225,1120225,2,1110045", "DCT-0250,1120250,1,1120011", "DCT-0250,1120250,2,1120012", "DCT-0250,1120250,3,1120013", "DCT-0260,1120260,1,1120021", "DCT-0260,1120260,2,1120022", "DCT-0260,1120260,3,1120023", "DCT-0265,1120265,1,1110050", "DCT-0265,1120265,2,1110055", "DCT-0280,1120280,1,1110060", "DCT-0280,1120280,2,1110065", "DCT-0300,1120300,1,1110070", "DCT-0300,1120300,2,1110075", "DCT-0305,1120305,1,1110080", "DCT-0310,1120310,1,1120031", "DCT-0310,1120310,2,1120032", "DCT-0310,1120310,3,1120033", "DCT-0310,1120310,4,1120041", "DCT-0310,1120310,5,1120042", "DCT-0310,1120310,6,1120043", "DCT-0320,1120320,1,1120051", "DCT-0320,1120320,2,1120052", "DCT-0320,1120320,3,1120053", "DCT-0330,1120330,1,1110085", "DCT-0330,1120330,2,1110090", "DCT-0390,1120390,1,1110095", "DCT-0420,1120420,1,1110100", "DCT-0440,1120440,1,1120061", "DCT-0440,1120440,2,1120062", "DCT-0440,1120440,3,1120063", "DCT-0445,1120445,1,1110105", "DCT-0460,1120460,1,1120071", "DCT-0460,1120460,2,1120072", "DCT-0460,1120460,3,1120073", "DCT-0460,1120460,4,1120074", "DCT-0465,1120465,1,1110110", "DCT-0466,1120466,1,1110115", "DCT-0466,1120466,2,1110120", "DCT-0490,1120490,1,1110125", "DCT-0490,1120490,2,1110130", "MID-0110,1140110,1,1120081", "MID-0110,1140110,2,1120082", "MID-0120,1140120,1,1110135", "DIFF-0100,1130100,1,1120091", "DIFF-0100,1130100,2,1120092", "DIFF-0100,1130100,3,1120101", "DIFF-0100,1130100,4,1120102", "DIFF-0110,1130110,1,1110140", "DCT-0540,1120540,1,1110145", "DCT-0635,1120635,1,1110150", "DCT-0636,1120636,1,1110155", "DCT-0638,1120638,1,1110160" };
            foreach (var item in data)
                opindices.Add(item.Split(','));

            // 2021/12/31 临时加入为了显示上下公差
            //2022/06/01 修改成数据库读取上下限
            //string[] data1 = { "1110005:10,12,N·m,0,90,°", "1110010:26,30,N·m,0,90,°", "1110015:10,12,N·m,0,90,°", "1110025:10,12,N·m,0,90,°", "1110030:10,12,N·m,0,90,°", "1110035:10,12,N·m,0,90,°", "1110040:10,12,N·m,0,90,°", "1110045:20,24,N·m,0,90,°", "1120011:20,24,N·m,0,90,°", "1120021:30,34,N·m,0,90,°", "1110050:30,34,N·m,0,90,°", "1110060:22,26,N·m,0,90,°", "1110065:10,12,N·m,0,90,°", "1110070:30,34,N·m,0,90,°", "1110080:30,34,N·m,0,90,°", "1120031:30,34,N·m,0,90,°", "1120051:20,24,N·m,0,90,°", "1110085:22,26,N·m,0,90,°", "1110090:30,34,N·m,0,90,°", "1110095:10,12,N·m,0,90,°", "1110100:43,47,N·m,0,90,°", "1120061:10,12,N·m,0,90,°", "1110105:10,12,N·m,0,90,°", "1120071:10,12,N·m,0,90,°", "1110110:10,12,N·m,0,90,°", "1103030:10,12,N·m,0,90,°", "1103031:20,24,N·m,0,90,°", "1110125:14,18,N·m,0,90,°", "1120081:114,126,N·m,0,90,°", "1110135:114,126,N·m,0,90,°", "1120091:85,95,N·m,0,90,°", "1120093:85,95,N·m,0,90,°", "1110145:10,12,N·m,0,90,°", "1110150:13,17,N·m,0,90,°", "1110155:30,40,N·m,0,90,°", "1110160:20,24,N·m,0,90,°" };
            //foreach (var item in data1)
            //    limits.Add(item.Split(':')[0], item.Split(':')[1].Split(','));
            // var k0100 = GetK0100("DCT-0460", "1");


            // 2022/01/10 添加的OP信息，OP信息来自于 https://docs.qq.com/sheet/DWVBJbWtIelZhZFNz?tab=BB08J2
            var op_strs = new string[] {"ID,OP Name","1,DCT-0440","2,MID-0110","3,DCT-0260","4,DCT-0320","5,","6,DIFF-0100","7,DCT-0310","8,DCT-0310","9,DCT-0460",
                "10,DIFF-0100","11,MID-0120","12,DCT-0225","13,DCT-0330","14,DCT-0330","15,DCT-0110","16,DCT-0300","17,DCT-0220","18,DCT-0280","19,DCT-0260",
                "20,DCT-0225","21,DCT-0100","22,DCT-0635","23,DCT-0080","24,DCT-0490","25,DCT-0390","26,","27,DCT-0300","28,DCT-0540","29,DCT-0490","30,",
                "31,DCT-0638","32,DCT-0270","33,DCT-0090","34,DCT-0420","35,DIFF-0110","36,DCT-0636","37,DCT-0490","38,DCT-0280","39,DCT-0300","40,",
                "41,DCT-0420","42,DCT-0390","43,DCT-0100","44,DCT-0090","45,MID-0120","46,DCT-0080","47,DCT-0110","48,DCT-0090","49,DCT-0330","50,","51,DIFF-0110",
                "52,DCT-0270","53,DCT-0225","54,DCT-0540","55,DCT-0225","56,DCT-0260","57,DCT-0635","58,DCT-0636","59,DCT-0638",
                "60,DCT-0300","61,","62,","63,"};
            foreach (var item in op_strs)
            {
                var strs = item.Split(',');
                if (strs.Length == 2 && !opNames.ContainsKey(strs[0]))
                    opNames.Add(strs[0], strs[1]);
            }


        }

        /// <summary>
        /// 加载工位号
        /// </summary>
        /// <param name="configfile"></param>
        private void LoadBenchNames(string configfile = "bench_names.txt")
        {
            configfile = Path.GetFullPath(configfile);
            List<string> names = new List<string>();
            List<string> texts = new List<string>();
            List<string> ips = new List<string>();

            if (!File.Exists(configfile))
                return;

            foreach (var row in File.ReadAllLines(configfile))
            {
                if (row.Length == 0)
                    continue;

                var strs = row.Trim().Replace('，', ',').Split(',');
                for (int i = 0; i < strs.Length; i++)
                    strs[i] = strs[i].Trim();
                if (strs.Length >= 2 && strs[0].Length > 0 && strs[1].Length > 0)
                {
                    names.Add(strs[0]);
                    texts.Add(strs[1]);
                    texts.Add(strs.Length > 2 ? strs[3] : "0.0.0.0");
                }
            }
            benchNames = names.Count > 0 ? names.ToArray() : benchNames;
            benchTexts = texts.Count > 0 ? texts.ToArray() : benchTexts;
            benchIPs = ips.Count > 0 ? ips.ToArray() : benchIPs;

        }


        public override bool OnStart()
        {
            if (DebugMode)
            {
                SqlConnectionString = "Data Source = 112.74.87.167;Initial Catalog = cvinet;User ID = sa; Password = root00;";
                sm = new SQLManager(SqlConnectionString);
            }

            // 加载工位信息K0010信息
            if (File.Exists(oplist_configfile))
            {
                oplist.Clear();
                var rows = File.ReadAllLines(oplist_configfile);
                // 注意：i=0时，是首行的标题，但ID是从1开始，所以不冲突。
                for (int i = 0; i < rows.Length; i++)
                    oplist.Add(rows[i].Split(','));
            }

            //获取数据灵活配置的上下限
            //string constr = "Data Source =10.0.7.186;Initial Catalog = chystat; User ID = sa; Password = Temp@pass;"; // 远程连接
            string constr = "Data Source =112.74.87.167;Initial Catalog = chystat; User ID = sa; Password = root00;"; // 远程连接

            psm = new SQLManager(constr);
            psm.CommandText = "SELECT code,low_value_k2110,high_value_k2111,cpk_level FROM chystat.dbo.properties WHERE tool_type = 2";
            var data = psm.GetStrings();


            foreach (var items in data)
            {
                //10,12,N·m,0,90,°
                Console.WriteLine(items[0].Trim());
                string[] arrLimit = { items[1].Trim().Split(';')[0], items[2].Trim().Split(';')[0], "N·m", items[1].Trim().Split(';')[1], items[2].Trim().Split(';')[1], "°" };

                limits.Add(items[0].Trim(), arrLimit);
            }


            // 由于开始ID为48500001，所以为了避免过多的等待，加入开始时自动检测第1个可用ID的功能。
            LastResID = GetNextID(LastResID);

            LoadBenchNames();
            return base.OnStart();
        }

        public override void OneLoop_Process()
        {
            // LastResID=48528106;
            // BatchCount=10;
            sm.CommandText = sqltemplate.Replace("{startID}", LastResID.ToString()).Replace("{endID}", (LastResID + BatchCount).ToString());
            sm.CommandText = $@"SELECT   
        dbo.RESULT.RES_ID,
        dbo.CONTROLLER.CTRL_Name AS K1001, 
        dbo.RESULT.RES_FinalTorque, 
        dbo.RESULT.RES_FinalAngle, 
        dbo.RESULT.RES_FinalCurrent, 
        dbo.RESULT.RES_FinalTorqueRate, 
        dbo.RESULT.RES_DateTime AS K0004, 
        dbo.RESULT.RES_VIN AS K0014, 
        dbo.TOOL.TOOL_SerialNumber AS K0012, 
        dbo.TIGHTENING_UNIT.TU_Name AS K0009, 
        dbo.RESULT.RES_ResultNumber, 
		dbo.RESULT.RES_SpindleNumber,
        dbo.RESULT.RES_ToolId AS OP_ID,
        dbo.CURVE.CRV_PointDuration, 
        dbo.CURVE.CRV_PointCount, 
        dbo.CURVE.CRV_Points,
        dbo.RESULT.RES_ControllerId,
        dbo.CURVE.CRV_BlobFormat
FROM    dbo.RESULT INNER JOIN
        dbo.CONTROLLER ON dbo.RESULT.RES_ControllerId = dbo.CONTROLLER.CTRL_ID INNER JOIN
        dbo.TOOL ON dbo.RESULT.RES_ToolId = dbo.TOOL.TOOL_ID INNER JOIN
        dbo.TIGHTENING_UNIT ON dbo.RESULT.RES_TuId = dbo.TIGHTENING_UNIT.TU_ID LEFT OUTER JOIN
        dbo.CURVE ON dbo.RESULT.RES_ID = dbo.CURVE.CRV_ResultID
WHERE   dbo.RESULT.RES_ID between {LastResID} and {LastResID + BatchCount}
ORDER BY dbo.RESULT.RES_ID"; // 为了提高效率，使用了基于Res_ID的 between-and 语法

            var data = sm.GetStrings();
            Common.AddLog("sql读取完毕", LogType.INFO);
            if (data == null || data.Length == 0)
                return;

            Dictionary<string, QFile> opFiles = new Dictionary<string, QFile>();

            var k2001s = new string[] { "FinalTorque", "FinalAngle", "FinalCurrent", "FinalTorqueRate" };
            var k2002s = new string[] { "扭矩", "角度", "电流", "扭矩率" };

            foreach (var row in data)
            {
                // Console.WriteLine(row[2] + ", " + row[3]);
                try
                {
                    // 00. RESULT.RES_ID,
                    // 01. CONTROLLER.CTRL_Name AS K1001, 
                    // 02. RESULT.RES_FinalTorque, 
                    // 03. RESULT.RES_FinalAngle, 
                    // 04. RESULT.RES_FinalCurrent, 
                    // 05. RESULT.RES_FinalTorqueRate, 
                    // 06. RESULT.RES_DateTime AS K0004, 
                    // 07. RESULT.RES_VIN AS K0014, 
                    // 08. TOOL.TOOL_SerialNumber AS K0012, 
                    // 09. TIGHTENING_UNIT.TU_Name AS K0009, 
                    // 10. RESULT.RES_ResultNumber, 
                    // 11. RESULT.RES_SpindleNumber
                    // 12. CONTROLLER.CTRL_ID AS OP_ID     # 2022/01/10 加入 为匹配OPName
                    // 13. dbo.CURVE.CRV_PointDuration,    # 2022/01/23 加入，用于处理曲线信息，14-15相同
                    // 14. dbo.CURVE.CRV_PointCount,
                    // 15. dbo.CURVE.CRV_Points
                    // 16. dbo.RESULT.RES_ControllerId     # 2022/04/13 如果OP_ID 不存在就使用此字段进行匹配
                    // 17. dbo.CURVE.CRV_BlobFormat        # 2023/09/20 当此值为2时表示压缩过，需要解压。
                    var list = new List<double>();
                    var testtime = DateTime.Parse(row[6]);
                    var RES_ResultNumber = row[10];   // 968854
                    var RES_SpindleNumber = row[11];  // 1
                    var CRV_PointCount = row[14];

                    var curveKey = $"{RES_SpindleNumber.Substring(0, 1)}{RES_ResultNumber.PadLeft(9, '0')}";
                    var curvefilename1 = $"B_01_{testtime:yyMMddHHmmss}_000_{curveKey}";
                    var curvefilename2 = $"B_02_{testtime:yyMMddHHmmss}_000_{curveKey}";


                    // 曲线不为空时，进行相应的处理。
                    if (row[15].StartsWith("System.Object:"))
                    {
                        var key = row[15].Split(':')[1];
                        var value = sm.NonStringData[key];


                        if (value != null && value is byte[] bts)
                        {                       
                            // Duration 表示测量持续时长，t_delta 使用 Duration/Count 计算而来。
                            var t_delta = row[13].Length > 0 ? double.Parse(row[13]) / int.Parse(row[14]) : 0;

                            // 为2时需要解压
                            if (row[17].Contains("2"))
                            {
                                var duration = int.Parse(row[13]);
                                List<CurvePoint> wavePoints = PointUtil.ConvertByteToPoints(
                                    ZipDecoder.DecompressZlibData(bts),
                                    ZipDecoder.ConvertToStride(duration));

                                StringBuilder sb1 = new StringBuilder();
                                StringBuilder sb2 = new StringBuilder();

                                // 测量值共有4个，分成2组，分别存储到2个文件中
                                for (int i = 0; i < wavePoints.Count; i++)
                                {
                                    var time_i = t_delta * i / 1000;
                                    sb1.Append($"{time_i},{wavePoints[i].Torque};");
                                    sb2.Append($"{time_i},{wavePoints[i].Angle};");
                                }

                                // 结果写到文件中，文件名分别如下：
                                // var curvefilename1 = $"B_01_{testtime:yyMMddHHmmss}_000_{curveKey}";
                                // var curvefilename2 = $"B_02_{testtime:yyMMddHHmmss}_000_{curveKey}";
                                File.WriteAllText(Path.Combine(OutputDirectory, curvefilename1 + ".txt"), sb1.ToString());
                                File.WriteAllText(Path.Combine(OutputDirectory, curvefilename2 + ".txt"), sb2.ToString());

                            }
                            else
                            {
                                StringBuilder sb = new StringBuilder();
                                for (int i = 0; i < bts.Length; i++)
                                    sb.Append(bts[i].ToString("X2"));
                                var s = sb.ToString();

                                for (int i = 0; i < s.Length / 72; i++)
                                {
                                    var start = i * 72;
                                    var s0 = s.Substring(start, 4);
                                    var s1 = s.Substring(start + 4, 16);
                                    var s2 = s.Substring(start + 20, 16);
                                    var s3 = s.Substring(start + 36, 16);
                                    var s4 = s.Substring(start + 52, 16);

                                    // 每6个值为一组，分别表示：序号，时间戳，测量值1~4
                                    list.Add(i);
                                    list.Add(t_delta * i);
                                    list.Add(Convert1(s1));
                                    list.Add(Convert1(s2));
                                    list.Add(Convert1(s3));
                                    list.Add(Convert1(s4));
                                }

                                StringBuilder sb1 = new StringBuilder();
                                StringBuilder sb2 = new StringBuilder();

                                // 测量值共有2个，分成2组，分别存储到2个文件中
                                for (int i = 0; i < list.Count / 6; i++)
                                {
                                    // 每个文件表示【单条曲线】，记录【时间戳】和【测量值】，时间除以1000表示单位是ms。
                                    sb1.Append($"{list[i * 6 + 1] / 1000},{list[i * 6 + 2]};"); // 
                                    sb2.Append($"{list[i * 6 + 1] / 1000},{list[i * 6 + 3]};"); // 
                                }

                                // 结果写到文件中，文件名分别如下：
                                // var curvefilename1 = $"B_01_{testtime:yyMMddHHmmss}_000_{curveKey}";
                                // var curvefilename2 = $"B_02_{testtime:yyMMddHHmmss}_000_{curveKey}";
                                File.WriteAllText(Path.Combine(OutputDirectory, curvefilename1 + ".txt"), sb1.ToString());
                                File.WriteAllText(Path.Combine(OutputDirectory, curvefilename2 + ".txt"), sb2.ToString());
                            }


                        }

                        continue;
                    }


                    string k1001 = "DF727-Temp";//  row[1]; 2022/01/13 由于张总无法从数据库中找到相应信息，所以为了演示临时写死。
                    string k0010 = "0";

                    // 2022/04/05 加入，用于通过检测台ID确定K0010
                    if (row[12] == null)
                    {
                        Common.AddLog("row[12]是null匹配不到K0010", LogType.INFO);
                    }
                    else
                    {
                        int ctrlID = Common.ParseInteger(row[12]);

                        if (ctrlID > 0 && ctrlID < oplist.Count)
                        {
                            k0010 = oplist[ctrlID][5];
                            k1001 = oplist[ctrlID][4];
                        }
                    }


                    var dt = DateTime.Parse(row[6]);
                    string k0014 = row[7];
                    string k0012 = row[8];
                    string k0009 = row[9];
                    string result_number = row[10];
                    string spindleNumber = row[11];
                    // string ctrl_id = row[12];

                    // 根据Controller.CTRL_ID从ops中获得OP Name，再通过catalog获得OP编号
                    // string op_name = opNames.ContainsKey(ctrl_id) ? op_name = opNames[ctrl_id] : "";

                    // 2021-12-29 加入用于提取K0100，
                    //string k0100 = GetK0100(k1001, spindleNumber);

                    // "1110010:26,30,N·m,0,90,°"
                    string[] values = limits.ContainsKey(k0010) ? limits[k0010] : new string[6];

                    QFile qf = opFiles.ContainsKey(k1001) ? opFiles[k1001] : new QFile();
                    if (qf.Charactericstics.Count == 0)
                    {
                        opFiles.Add(k1001, qf);
                        qf[1001] = k1001;
                        qf[1002] = k1001;
                        qf[1086] = k1001;

                        // 4 -> 2 暂时只考虑扭距和角度
                        for (int i = 0; i < 2; i++)
                        {
                            var chi = qf.AddQCharacteristic();
                            chi[2001] = k2001s[i];
                            chi[2002] = k2002s[i];
                            chi[2110] = values[i * 3 + 0];
                            chi[2111] = values[i * 3 + 1];
                            chi[2142] = values[i * 3 + 2];
                        }
                    }

                    if (row[2].ToString().Length > 0 && row[2].ToString().Length > 0)
                    {
                        string[] filenames = { curvefilename1, curvefilename2 };
                        for (int i = 0; i < 2; i++)
                        {
                            var chi = qf.Charactericstics[i];
                            var di = chi.AddItem();
                            di.SetValue(row[2 + i]);
                            di.date = dt;
                            di[0009] = k0009;
                            // di[0010] = k0100; // 2021/12/29 更新 原为固定值 "1120071";
                            di[0010] = k0010; // 2022/04/05 更新;
                            di[0012] = k0012;
                            di[0014] = k0014;
                            di[0017] = filenames[i];
                        }
                    }
                }
                catch (Exception ex)
                {
                    Common.AddLog(ex.Message, LogType.Error);
                }
            }

            foreach (QFile qf in opFiles.Values)
            {
                string folder = OutputDirectory;
                if (!Directory.Exists(folder))
                    Directory.CreateDirectory(folder);
                SaveToDFQ(qf, folder, $"CVINET_{qf[1001]}_{DateTime.Now:yyyyMMddHHmmss}.dfq");
            }

            ia.WriteValue(Config.LastResID, LastResID.ToString());
        }

        /// <summary>
        /// 持续转换，永远返回 false。
        /// </summary>
        /// <returns></returns>
        public override bool IsLoopCompleted() => false;

        internal double Convert1(string v)
        {
            var bts = new byte[8];
            for (int i = 0; i < v.Length / 2; i++)
                bts[i] = System.Convert.ToByte(v.Substring(i * 2, 2), 16);

            MemoryStream ms = new MemoryStream(bts);
            BinaryReader br = new BinaryReader(ms);
            var d = br.ReadDouble();
            br.Close();
            return d;
        }


        // 修改代码，现在临时从 opindices 中匹配，根据K1001和SpindleNumber来匹配OP索引
        private string GetK0100(string k1001, string spindleNumber)
        {
            var k0100 = k1001;
            // K0100的主要格式有两种：[DIFF1002R]@192.168.21.156 和 DCT-0225
            if (k1001.Contains("@"))
                k0100 = k1001.Split('@')[0];
            k0100 = k0100.Trim('[', ']');

            if (opindices.Count(op => (op[0].Contains(k0100) || k0100.Contains(op[0])) && op[2] == spindleNumber) > 0)
            {
                var row = opindices.First(op => op[0].Contains(k0100) && op[2] == spindleNumber);
                return row[3];
            }

            int pid = catlog.getCatalogPID("K4090", k0100);
            return pid < 0 ? "" : catlog.GetValue("K4091", pid);
        }


        /// <summary>
        /// 整个查询字符串。
        /// 2019-3-15 加入查询 Result.PositionID 和 vUnitProgramBolt.UnitName
        /// </summary>
        /// <returns></returns>
        private string GetSql()
        {
            return $@"SELECT TOP {BatchCount}
	ResultTightening.ResultID, 
	ResultTightening.FinalAngle, 
	ResultTightening.FinalTorque, 
	Result.ResultDateTime, 
	Result.ProgramID, 
	ResultIdentifier.Identifier,
    'UnitName' as UnitName, -- vUnitProgramBolt.UnitName,
    'BoltName' as BoltName,--vUnitProgramBolt.BoltName,
    'SystemType' as SystemType,--vUnitProgramBolt.SystemType,
    ProgramParameter.ParameterValue,
    ProgramParameter.LimitHigh,
    ProgramParameter.LimitLow,
    Result.PositionID,
    'ProgramName' as UnitName, --vUnitProgramBolt.ProgramName,
    ResultStep.ResultStepID
FROM 
	ACDC.Result INNER JOIN
	ACDC.ResultTightening ON ACDC.Result.ID = ACDC.ResultTightening.ResultID INNER JOIN
	-- vUnitProgramBolt ON ACDC.Result.ID = vUnitProgramBolt.ResultId INNER JOIN
	ACDC.ResultToResultIdentifier ON ACDC.ResultToResultIdentifier.ResultID = ACDC.Result.ID INNER JOIN
	ACDC.ResultIdentifier ON ACDC.ResultToResultIdentifier.ResultIdentifierID = ACDC.ResultIdentifier.ID INNER JOIN
	ACDC.ProgramParameter ON ACDC.Result.ProgramID = ACDC.ProgramParameter.ProgramID LEFT OUTER JOIN
    ACDC.ResultStep ON ACDC.Result.ID = ACDC.ResultStep.ResultID
WHERE 
	ACDC.ProgramParameter.ProgramParameterTypeID in (1,2) and ACDC.ResultTightening.ResultID >= {LastResID}
ORDER BY
	ACDC.ResultTightening.ResultID ";
        }


        /// <summary>
        /// 根据K2002的值，查询对应的参数。
        /// </summary>
        /// <param name="qf"></param>
        /// <param name="k2002"></param>
        /// <returns></returns>
        private QCharacteristic GetCharacteristic(QFile qf, string k2001, string k2002)
        {
            QCharacteristic q = new QCharacteristic();
            if (qf.Charactericstics.Count(qc => (qc[2001]?.ToString() == k2001 && qc[2002]?.ToString() == k2002)) > 0)
            {
                q = qf.Charactericstics.First(qc => (qc[2001]?.ToString() == k2001 && qc[2002]?.ToString() == k2002));
            }
            else
            {
                q[2001] = k2001;
                q[2002] = k2002;
                qf.Charactericstics.Add(q);
            }

            return q;
        }


        private string getBoltSerial(string[] row)
        {
            string acturalBoltSerial = "1";
            try
            {
                int cycleOkCount = int.Parse(row[KField.RES_CycleOKCount]);
                int spindleNumber = int.Parse(row[KField.RES_SpindleNumber]);
                int spindleCount = int.Parse(row[KField.RES_SpindleCount]);
                int boltCount = 1;
                try
                {
                    boltCount = int.Parse(row[KField.BoltCount]);
                }
                catch { }
                return ((cycleOkCount - 1) * spindleCount + spindleNumber).ToString() + '_' + boltCount;
            }
            catch (Exception ex)
            {
                AddLog(LogType.Error, ex.Message);
            }

            return acturalBoltSerial;
        }

        private string GetK0007(string str)
        {
            string res = null;
            foreach (var item in benchNames)
            {
                if (str.Contains(item))
                {
                    res = item;
                    break;
                }
            }
            return res;
        }

        string sqltemplate = @"SELECT TOP(1000)
	dbo.RESULT.RES_ID,
	dbo.CONTROLLER.CTRL_ID,
	dbo.CONTROLLER.CTRL_Name AS K0100,
	dbo.CONTROLLER.CTRL_IPAddress,
	dbo.RESULT.RES_SpindleNumber,
	dbo.RESULT.RES_VIN,
	dbo.RESULT.RES_DateTime,
	dbo.RESULT.RES_BoltNumber,
	dbo.RESULT.RES_FinalTorque,
	dbo.STEP.STEP_TorqueTarget,
	dbo.STEP.STEP_TorqueMinTolerance,
	dbo.STEP.STEP_TorqueMaxTolerance,
	dbo.RESULT.RES_FinalAngle,
	dbo.STEP.STEP_AngleTarget,
	dbo.STEP.STEP_AngleMinTolerance,
	dbo.STEP.STEP_AngleMaxTolerance,
	dbo.RESULT.RES_Report,
	'CavityNumber' AS K0007,
	'OperatorName' AS K0008,
	'MachineNumber' AS K0010m,
	'GroupNumber' AS K0012,
	dbo.CONTROLLER.CTRL_Name AS K0053,
	'PartNo' AS K1001, 
	dbo.RESULT.RES_PsetId,
	dbo.PSET.PS_Comment,
	dbo.TOOL.TOOL_SerialNumber,
	dbo.PSET.PS_Number, 
    dbo.RESULT.RES_CycleOKCount,
    dbo.RESULT.RES_SpindleCount,
    'K1002' AS K1002
FROM
	dbo.RESULT
    INNER JOIN dbo.CONTROLLER ON dbo.RESULT.RES_ControllerId = dbo.CONTROLLER.CTRL_ID
    INNER JOIN dbo.PSET ON dbo.RESULT.RES_PsetId = dbo.PSET.PS_ID
    INNER JOIN dbo.TOOL ON dbo.RESULT.RES_ToolId = dbo.TOOL.TOOL_ID
    INNER JOIN dbo.STEP ON dbo.RESULT.RES_StepId = dbo.STEP.STEP_Id
WHERE
    RES_ID >= {startID} and RES_ID <= {endID}
Order BY
    RES_ID";

        class KField
        {
            // 所有用到的K域
            public static int RES_ID = 0;
            public static int CTRL_ID = 1;
            public static int CTRL_Name = 2;
            public static int CTRL_IPAddress = 3;
            public static int RES_SpindleNumber = 4;
            public static int RES_VIN = 5;
            public static int RES_DateTime = 6;
            public static int RES_BoltNumber = 7;
            public static int RES_FinalTorque = 8;
            public static int STEP_TorqueTarget = 9;
            public static int STEP_TorqueMinTolerance = 10;
            public static int STEP_TorqueMaxTolerance = 11;
            public static int RES_FinalAngle = 12;
            public static int STEP_AngleTarget = 13;
            public static int STEP_AngleMinTolerance = 14;
            public static int STEP_AngleMaxTolerance = 15;
            public static int RES_Report = 16;
            public static int K0007 = 17;
            public static int K0008 = 18;
            public static int K0010 = 19;
            public static int K0012 = 20;
            public static int K0053 = 21;
            public static int K1001 = 22;
            public static int RES_PsetId = 23;
            public static int PS_Comment = 24;
            public static int TOOL_SerialNumber = 25;
            public static int PS_Number = 26;
            public static int RES_CycleOKCount = 27;
            public static int RES_SpindleCount = 28;
            public static int BoltCount = 29;
            public static int K1002 = 5;
            public static int PSETNUMBER = 27;
        }

    }

}
